//
//  WalkNodeBase.cpp
//  ChampionRun
//
//  Created by MHD Yamen Saraiji on 11/10/13.
//
//

#include "WalkNodeBase.h"

namespace cocos2d
{

std::vector<std::string> WalkNodeBase::s_TrainingLabels;
bool WalkNodeBase::s_trainingLabelInit=false;

 const  std::vector<std::string> & WalkNodeBase::GetTrainingLabels()
{
    if(!s_trainingLabelInit)
    {
        s_trainingLabelInit=true;
        s_TrainingLabels.push_back("Standing");
        s_TrainingLabels.push_back("Slow Walk");
        s_TrainingLabels.push_back("Fast Walk");
        s_TrainingLabels.push_back("Running");
    }
    return s_TrainingLabels;
}

WalkNodeBase::WalkNodeBase()
{
    m_time=0;
    scheduleUpdate();
    m_added=false;
    int samplesCount=128;
    m_frequency=2.0f/samplesCount;
    
    m_accelerationSamples[0].resize(samplesCount, 0);
    m_accelerationSamples[1].resize(samplesCount, 0);
    m_accelerationSamples[2].resize(samplesCount, 0);
    m_fft[0]=CFft::create(samplesCount, OF_FFT_WINDOW_HAMMING,OF_FFT_BASIC);
    m_fft[1]=CFft::create(samplesCount, OF_FFT_WINDOW_HAMMING,OF_FFT_BASIC);
    m_fft[2]=CFft::create(samplesCount, OF_FFT_WINDOW_HAMMING,OF_FFT_BASIC);
    
    int binSize=m_fft[0]->getBinSize();
    m_histogram.resize(binSize);
    
    m_mean=0;
    m_stdDev=0;
    
    int n=20;
    int windowSize=binSize/n;
    for(int i=0;i<n;++i)
    {
        WaveWindow* wnd=new WaveWindow(i*windowSize,(i+1)*windowSize);
        m_windows.push_back(wnd);
    }
    /*
     m_windows.push_back(new WaveWindow(0,binSize/4.0f,"Stand",0.1));
     m_windows.push_back(new WaveWindow(binSize/4.0f,binSize/2.0f,"SlowWalk",0.5));
     m_windows.push_back(new WaveWindow(binSize/2.0f,binSize*3.0f/4.0f,"FastWalk",0.5));
     m_windows.push_back(new WaveWindow(binSize*3.0f/4.0f,binSize,"Running",0.5));
     */
    
}


void WalkNodeBase::_updateHistogram()
{
    _dirty=true;
    
    AccelerationList::iterator it=m_accelerations.begin();
    
    for (int i=0; it!=m_accelerations.end(); ++it,++i) {
        Acceleration& acc=*it;
        m_accelerationSamples[0][i]=acc.x;
        m_accelerationSamples[1][i]=acc.y;
        m_accelerationSamples[2][i]=acc.z;
    }
    
    m_fft[0]->setSignal(m_accelerationSamples[0]);
    m_fft[1]->setSignal(m_accelerationSamples[1]);
    m_fft[2]->setSignal(m_accelerationSamples[2]);
    float* amps[3];
    amps[0]=m_fft[0]->getAmplitude();
    amps[1]=m_fft[1]->getAmplitude();
    amps[2]=m_fft[2]->getAmplitude();
    int binSize=m_histogram.size();
    
    //apply guassian filter on samples
    for(int i=0;i<binSize;++i)
    {
    }
    
    float max=0;
    m_mean=0;
    
    for(int i=0;i<binSize;++i)
    {
        m_histogram[i]=(amps[0][i]+amps[1][i]+amps[2][i])/3.0f;
        if(m_histogram[i]>max)
            max=m_histogram[i];
    }
    
    float invMax=1.0f/max;
    
    float midPoint=0;
    //normalize
    for(int i=0;i<binSize;++i)
    {
        m_histogram[i]*=invMax;
        m_mean+=m_histogram[i];
        midPoint+=m_histogram[i]*i/(float)binSize;
    }
    m_mean/=(float)binSize;
    m_stdDev=0;
    
    for(int i=0;i<binSize;++i)
    {
        
        float diff=(m_histogram[i]-m_mean);
        //    m_histogram[i]=diff;
        m_stdDev+=diff*diff;
    }
    m_stdDev/=(float)binSize;
    
    m_mean=midPoint;
    
    //calculate windows
    for(int i=0;i<m_windows.size();++i)
    {
        m_windows[i]->Calculate(&m_histogram[0]);
    }
}


void WalkNodeBase::draw()
{
    CC_NODE_DRAW_SETUP();;
    GL::blendFunc(_blendFunc.src, _blendFunc.dst);
    
    _render();
    
}
void WalkNodeBase::_render()
{
    if (_dirty)
    {
        glBindBuffer(GL_ARRAY_BUFFER, _vbo);
        glBufferData(GL_ARRAY_BUFFER, sizeof(V2F_C4B_T2F)*_bufferCapacity, _buffer, GL_STREAM_DRAW);
        _dirty = false;
    }
#if CC_TEXTURE_ATLAS_USE_VAO
    GL::bindVAO(_vao);
#else
    GL::enableVertexAttribs(GL::VERTEX_ATTRIB_FLAG_POS_COLOR_TEX);
    
    glBindBuffer(GL_ARRAY_BUFFER, _vbo);
    // vertex
    glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, sizeof(V2F_C4B_T2F), (GLvoid *)offsetof(V2F_C4B_T2F, vertices));
    
    // color
    glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(V2F_C4B_T2F), (GLvoid *)offsetof(V2F_C4B_T2F, colors));
    //
    // texcood
    glVertexAttribPointer(GLProgram::VERTEX_ATTRIB_TEX_COORDS, 2, GL_FLOAT, GL_FALSE, sizeof(V2F_C4B_T2F), (GLvoid *)offsetof(V2F_C4B_T2F, texCoords));
#endif
    
    glDrawArrays(GL_LINE_STRIP, 0, m_fft[0]->getBinSize());
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    
    CC_INCREMENT_GL_DRAWS(1);
    CHECK_GL_ERROR_DEBUG();
}
void WalkNodeBase::_updateBuffer()
{
    V2F_C4B_T2F *vertex = (V2F_C4B_T2F *)(_buffer + _bufferCount);
    float invWidth=getContentSize().width/(float)m_histogram.size();
    
    int count=m_histogram.size();
    ensureCapacity(count);
    for(int i=0;i<count;++i)
    {
        Point pt(i*invWidth+getPositionX(),m_histogram[i]*getContentSize().height+getPositionY());
        vertex->vertices.x=pt.x;
        vertex->vertices.y=pt.y;
        vertex->colors=Color4B(255-255*m_histogram[i], 255, 255, 255);
        vertex++;
    }
}



void WalkNodeBase::OnAcceleration(Acceleration* acc)
{
    if(m_added)
        return;
    m_added=true;
    m_accelerations.push_back(*acc);
    if(m_accelerations.size()>m_accelerationSamples[0].size())
    {
        m_accelerations.erase(m_accelerations.begin());
    }
    
    _updateHistogram();
    _updateBuffer();
}

void WalkNodeBase::update(float dt)
{
    DrawNode::update(dt);
    
    m_time+=dt;
    if(m_time>m_frequency)
    {
        m_added=false;
        m_time-=m_frequency;
    }
}


}